//bind模拟
/*
* bind() 方法会创建一个新函数。当这个新函数被调用时，bind() 的第一个参数将作为它运行时的 this，之后的一序列参数将会在传递的实参前传入作为它的参数
* bind函数：
* 1.返回一个函数
* 2.可以传入参数
* function.bing(thisArg,arg1,arg2,..)
*
* 实现思路:
* 1.返回一个新函数(闭包)
* 2.新函数执行时,this指向指定的上下文
* 3.支持参数合并(柯里化)
* 4.处理构造函数调用情况
*
* 关键特性:
* 返回新函数，原函数不变
* 支持this绑定
* 支持参数柯里化
* 正常处理构造函数调用
* 保持函数原型链
* */

/*示例：*/
const person = {
    name: 'John',
    age: 30
};

function greet(greeting, punctuation) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}

// 原生 bind 使用
const boundGreet = greet.bind(person, 'Hello');
boundGreet('!'); // 输出: Hello, John!

//mock

Function.prototype.myNewBind = function (context,...args){
    // 保存原函数
    const originalFunc = this;
    //定义边界函数
    const boundFunction = function(...innerArgs){
        const allArgs = [...args,...innerArgs];
        // 判断是否通过new 调用
        // 如果是通过new 调用,this指向新创建的对象,否则使用传入的context;
        const isNewCall = this instanceof  boundFunction;
        const thisContext = isNewCall ?this:context;
        return originalFunc.apply(thisContext,allArgs);
    }
    //维护原型关系 (支持 new 操作符)
    // 使用空函数作为中介,避免直接修复 boundFunction.prototype
    const F = function(){};
    F.prototype = originalFunc.prototype;
    boundFunction.prototype = new F();
    return boundFunction;
}


Function.prototype.myBind = function (context){
    if(typeof this !== "function"){
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    //保持原函数
    const self = this;
    const args = Array.prototype.slice.call(arguments,1);
    const fNOP = function () {}
    const fBound = function (){
        const bindArgs = Array.prototype.slice.call(arguments);
        //当作为构造函数时,this指向实例,此时结果为true
        //当作为普通函数时,this指向window,此时结果为false
        return self.apply(this instanceof fNOP?this:context,args.concat(bindArgs));
    }
    //维护原型关系
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
